#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@Author          : ricsy
@File            : logger_utils.py
@Create Time     : 2025/6/2 19:11
@Last Modified   :
@Description     :
"""
import inspect
import logging
import os
import re
import threading
import time
from logging.handlers import RotatingFileHandler
from pathlib import Path

from colorama import Fore, Style, init


project_root = Path(__file__).parents[1]
# 初始化 colorama,自动重置颜色
init(autoreset=True)


class CustomLogger(logging.Logger):
    """
    自定义
    """

    # pylint: disable=too-many-arguments,too-many-positional-arguments
    def makeRecord(
        self,
        name,
        level,
        fn,
        lno,
        msg,
        args,
        exc_info,
        func=None,
        extra=None,
        sinfo=None,
    ):
        """
        重写makeRecord方法,添加文件名称、函数名称、行号等到字典中
        """
        level_name_dict = {
            logging.DEBUG: "DEBUG",
            logging.INFO: "INFO",
            logging.WARNING: "WARN",
            logging.ERROR: "ERROR",
            logging.CRITICAL: "CRITICAL",
        }
        if extra is None:
            extra = {}
        stack = inspect.stack()
        caller_frame = stack[4][0]
        extra["file_name"] = os.path.basename(caller_frame.f_code.co_filename)
        extra["func_name"] = caller_frame.f_code.co_name.replace("<module>", "")
        extra["line_no"] = caller_frame.f_lineno
        extra["level_name"] = level_name_dict.get(level, "INFO")
        return super().makeRecord(
            name, level, fn, lno, msg, args, exc_info, func, extra, sinfo
        )


class LoggerUtils:
    # pylint: disable=too-many-arguments,too-many-positional-arguments
    def __init__(
        self,
        file_name=f"{project_root}/logs/main.log",
        log_name=None,
        f_level=logging.INFO,
        s_level=logging.INFO,
        add_handler=True,
        with_func_name=True,
        with_time=False,
        file_with_color=False,
        max_file_size=1024 * 1024 * 10,
        backup_count=5,
    ):
        """
        NOTSET < DEBUG(10) < INFO(20) < WARNING(30) < ERROR(40) < CRITICAL(50)
        当设置一个级别时，只有该级别及更高优先级的日志会被记录
        :param log_name: 日志名称，默认为 None, 默认为 logger_utils
        :param file_name: 日志文件路径
        :param f_level:  文件日志级别，用于持久化记录
        :param s_level:  控制台日志级别，用于及时调试
        :param add_handler: 是否为 Logger 实例添加处理器
        :param with_func_name: 日志是否带函数名称
        :param with_time: 日志是否带时间
        :param file_with_color: 日志文件是否带颜色
        :param max_file_size: 单个日志文件的最大大小（字节），默认为 10MB（1024 * 1024 * 10）
        :param backup_count: 保留的备份文件数量，默认为 5
        :return: 日志对象
        """
        self.file_with_color = file_with_color
        # 获取 logger 对象
        self.main_logger = CustomLogger(log_name)
        # 设置日志级别（默认DEBUG）
        self.main_logger.setLevel(10)
        # 创建日志格式化器，示例：[2023-08-31 14:16:14] [INFO] This is an info message
        if with_func_name:
            self.fmt = (
                "[%(asctime)s] [%(level_name)s] "
                "[%(file_name)s:%(func_name)s:%(line_no)d] %(message)s"
            )
        else:
            self.fmt = "[%(asctime)s] [%(level_name)s] %(message)s"
        self.with_time = with_time
        self.datefmt = "%Y-%m-%d %H:%M:%S"
        self.formatter = logging.Formatter(self.fmt, self.datefmt)
        if add_handler:
            # 创建日志处理程序（控制台输出）
            self.console_handler = logging.StreamHandler()
            # 设置日志级别
            self.console_handler.setLevel(s_level)
            # 将格式化器应用到处理程序
            self.console_handler.setFormatter(self.formatter)
            # 将处理程序添加到 logger 对象
            self.main_logger.addHandler(self.console_handler)

            if file_name:
                # 获取文件路径的目录部分
                dir_name = os.path.dirname(file_name)
                if dir_name:
                    os.makedirs(dir_name, exist_ok=True)
                    # 创建日志处理程序（写入文件,允许写入和读取）
                    self.file_handler = RotatingFileHandler(
                        file_name,
                        "a",
                        maxBytes=max_file_size,
                        backupCount=backup_count,
                        encoding="utf-8",
                    )
                    # 将格式化器应用到处理程序
                    self.file_handler.setFormatter(self.formatter)
                    # 设置日志级别
                    self.file_handler.setLevel(f_level)
                    self.main_logger.addHandler(self.file_handler)

        self.log_colors = {
            logging.DEBUG: Fore.BLUE + Style.BRIGHT,
            logging.INFO: Fore.RESET + Style.BRIGHT,
            logging.WARNING: Fore.YELLOW + Style.BRIGHT,
            logging.ERROR: Fore.RED + Style.BRIGHT,
            logging.CRITICAL: Fore.MAGENTA + Style.BRIGHT,
        }
        self.set_color_formatter()
        self._local = threading.local()  # 添加线程本地存储

    def get_logger(self):
        return self.main_logger

    def _colorize(self, level):
        return self.log_colors.get(level, "")

    @staticmethod
    def _reset_color():
        return Fore.RESET + Style.RESET_ALL

    def _with_color_format(self, record):
        """
        日志使用颜色进行高亮
        :param record:
        :return:
        """
        log_color = self._colorize(record.levelno)
        reset_color = self._reset_color()
        message = self.formatter.format(record)
        return f"{log_color}{message}{reset_color}"

    def _without_color_format(self, record):
        """
        日志不使用颜色进行高亮
        :param record:
        :return:
        """
        message = self.formatter.format(record)
        # 使用正则表达式移除内容中 ANSI 颜色代码
        ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
        return ansi_escape.sub("", message)

    def set_color_formatter(self):
        for handler in self.main_logger.handlers:
            # FileHandler 是 StreamHandler 的子类,所以这里使用：isinstance 判断处理器是否为FileHandler
            if isinstance(handler, logging.FileHandler):
                file_formatter = logging.Formatter(self.fmt, self.datefmt)
                file_formatter.format = (
                    self._with_color_format
                    if self.file_with_color
                    else self._without_color_format
                )
                handler.setFormatter(file_formatter)
            else:
                color_formatter = logging.Formatter(self.fmt, self.datefmt)
                color_formatter.format = self._with_color_format
                handler.setFormatter(color_formatter)

    def _add_time_delta(self, message):
        """为日志信息添加时间差"""
        if not self.with_time:
            return message
        now = time.time()
        last_time = getattr(self._local, "last_log_time", None)
        delta = now - last_time if last_time else 0
        self._local.last_log_time = now
        return f"[+{delta:.3f}s] {message}"

    def debug(self, message):
        message = self._add_time_delta(message)
        self.main_logger.debug(msg=message)

    def info(self, message, color_type=0):
        """
        :param message: 日志内容
        :param color_type: 日志颜色类型（0 一般内容 | 1 蓝色 - 重要步骤 | 2 绿色 - 处理结果）
        """
        message = self._add_time_delta(message)
        if color_type == 0:
            colored_message = message
        elif color_type == 1:
            colored_message = f"{Fore.BLUE}{Style.BRIGHT}{message}{Fore.RESET}"
        elif color_type == 2:
            colored_message = f"{Fore.GREEN}{Style.BRIGHT}{message}{Fore.RESET}"
        else:
            colored_message = message
        self.main_logger.info(colored_message, extra={"color_type": color_type})

    def warning(self, message):
        message = self._add_time_delta(message)
        self.main_logger.warning(msg=message)

    def error(self, message, exc_info=False):
        """
        error 默认包含异常的堆栈跟踪信息
        :param message:
        :param exc_info:
        :return:
        """
        message = self._add_time_delta(message)
        self.main_logger.error(msg=message, exc_info=exc_info)

    def critical(self, message):
        """
        critical 默认不包含异常的堆栈跟踪信息
        :param message:
        :return:
        """
        message = self._add_time_delta(message)
        self.main_logger.critical(msg=message)

    def exception(self, message):
        """
        exception 默认包含异常的堆栈跟踪信息
        :param message:
        :return:
        """
        message = self._add_time_delta(message)
        self.main_logger.exception(msg=message)


# 使用示例
if __name__ == "__main__":
    logger = LoggerUtils(f_level=logging.INFO, file_with_color=True)
    logger.debug("This is a debug message")
    logger.info("This is an info message")
    logger.info("This is a step message", 1)
    logger.info("This is result message", 2)
    logger.warning("This is a warning message")
    logger.error("This is an error message")
    logger.critical("This is a critical message")
